import os
import sys
import glob
import csv
import chardet
import pandas as pd
from collections import defaultdict
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, 
    QPushButton, QListWidget, QFileDialog, QMessageBox, QComboBox, QRadioButton, 
    QButtonGroup, QTextEdit, QProgressBar, QGroupBox, QCheckBox, QSplitter, QTableWidget,
    QTableWidgetItem, QHeaderView
)
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QDialog

class CSV_Merger:
    def __init__(self):
        self.files = []
        self.merged_data = []
        self.headers = []
        self.raw_headers = []  # 存储原始列名
        self.encoding_cache = {}
        self.duplicates = []
        self.invalid_rows = []  # 存储无效行数据
        self.abnormal_rows = []  # 存储异常行数据
    
    def detect_encoding(self, file_path):
        """自动检测文件编码"""
        if file_path in self.encoding_cache:
            return self.encoding_cache[file_path]
        
        try:
            with open(file_path, 'rb') as f:
                raw_data = f.read(10000)  # 读取前10000字节用于检测
                result = chardet.detect(raw_data)
                encoding = result['encoding'] or 'utf-8'
                self.encoding_cache[file_path] = encoding
                return encoding
        except Exception as e:
            print(f"检测编码失败: {str(e)}")
            return 'utf-8'

    def find_files(self, directory, keyword):
        """递归查找目录下的CSV文件"""
        pattern = os.path.join(directory, '**', f'*{keyword}*.csv')
        return [f for f in glob.glob(pattern, recursive=True)]

    def read_csv_headers(self, file_path):
        """读取CSV文件的列名并进行标准化处理"""
        encoding = self.detect_encoding(file_path)
        try:
            with open(file_path, 'r', encoding=encoding, errors='replace') as f:
                reader = csv.reader(f)
                headers = next(reader)
                # 标准化处理：去除首尾空格、不可见字符，统一大小写
                return [header.strip().replace('\ufeff', '').replace('\u200b', '').upper() for header in headers]
        except Exception as e:
            print(f"读取文件头失败: {str(e)}")
            return []
    
    def get_raw_headers(self, file_path):
        """获取原始列名（不进行标准化处理）"""
        encoding = self.detect_encoding(file_path)
        try:
            with open(file_path, 'r', encoding=encoding, errors='replace') as f:
                reader = csv.reader(f)
                return next(reader)
        except Exception as e:
            print(f"读取原始文件头失败: {str(e)}")
            return []

    def check_headers_consistency(self):
        """检查所有文件的列名一致性"""
        if not self.files:
            return False, "没有文件可检查"
            
        header_groups = defaultdict(list)
        raw_header_groups = defaultdict(list)  # 存储原始列名分组
        
        for file in self.files:
            try:
                # 获取标准化列名用于比较
                headers = tuple(self.read_csv_headers(file))
                # 获取原始列名用于显示
                raw_headers = tuple(self.get_raw_headers(file))
                
                if headers:
                    header_groups[headers].append(os.path.basename(file))
                    # 保存原始列名分组
                    raw_header_groups[headers] = raw_headers
            except Exception as e:
                return False, f"读取文件 {os.path.basename(file)} 失败: {str(e)}"
        
        if len(header_groups) == 0:
            return False, "没有有效的文件头"
        
        if len(header_groups) > 1:
            msg = "警告: 文件列名不一致!\n"
            msg += "=" * 80 + "\n"
            for idx, (std_headers, files) in enumerate(header_groups.items()):
                raw_headers = raw_header_groups[std_headers]
                msg += f"分组 {idx+1} - 列数: {len(std_headers)}\n"
                msg += f"文件: {', '.join(files)}\n"
                msg += f"原始列名: {', '.join(raw_headers)}\n"
                msg += f"标准化后: {', '.join(std_headers)}\n"
                msg += "-" * 80 + "\n"
            return False, msg
        
        # 保存公共列名（使用第一个文件的原始列名）
        first_file = self.files[0]
        self.raw_headers = list(self.get_raw_headers(first_file))
        self.headers = list(next(iter(header_groups.keys())))
        return True, "所有文件列名一致"

    def is_valid_row(self, row, key_index):
        """检查行数据是否有效（非键值列不能全为0或空）"""
        # 跳过键值列
        for i, value in enumerate(row):
            if i == key_index:
                continue
                
            # 检查值是否有效（非空且非0）
            if value and value.strip() != "" and value.strip() != "0":
                return True
                
        return False

    def is_abnormal_row(self, row, columns):
        """检查行数据是否异常（指定列的值全部相同）"""
        if not columns:
            return False
            
        values = set()
        for col in columns:
            try:
                # 使用标准化列名查找索引
                col_upper = col.strip().upper()
                idx = self.headers.index(col_upper)
                value = row[idx].strip()
                if value:  # 只考虑非空值
                    values.add(value.lower())
            except (ValueError, IndexError):
                # 如果列名不存在或索引越界，跳过该列
                continue
                
        # 如果所有值相同（且至少有两个列）
        return len(values) == 1 and len(columns) > 1

    def merge_files(self, key_column, sort_column=None, ascending=True, filter_zero=True, abnormal_columns=None):
        """合并CSV文件并进行排序"""
        self.merged_data = []
        self.duplicates = []
        self.invalid_rows = []  # 重置无效行列表
        self.abnormal_rows = []  # 重置异常行列表
        
        if not self.files:
            return False, "错误: 没有选择文件"
        
        # 检查列名一致性
        success, msg = self.check_headers_consistency()
        if not success:
            return False, msg
        
        # 验证键值列是否存在
        key_column_upper = key_column.strip().upper()
        if key_column_upper not in self.headers:
            return False, f"错误: 键值列 '{key_column}' 不存在"
        
        # 获取键值列索引
        key_index = self.headers.index(key_column_upper)
        
        # 合并数据
        seen_keys = defaultdict(list)
        total_rows = 0
        invalid_count = 0
        abnormal_count = 0
        
        for file in self.files:
            encoding = self.detect_encoding(file)
            try:
                with open(file, 'r', encoding=encoding, errors='replace') as f:
                    reader = csv.reader(f)
                    next(reader)  # 跳过标题行
                    
                    for row in reader:
                        if len(row) != len(self.headers):
                            continue
                        
                        total_rows += 1
                        
                        # 数据校验：如果启用了过滤并且行无效
                        if filter_zero and not self.is_valid_row(row, key_index):
                            invalid_count += 1
                            self.invalid_rows.append({
                                'file': os.path.basename(file),
                                'row': row
                            })
                            continue
                            
                        # 异常检测：如果启用了异常检测且行异常
                        if abnormal_columns and self.is_abnormal_row(row, abnormal_columns):
                            abnormal_count += 1
                            self.abnormal_rows.append({
                                'file': os.path.basename(file),
                                'row': row
                            })
                            continue  # 跳过异常行，不包含在最终结果中
                            
                        key_val = row[key_index]
                        existing = False
                        
                        # 检查键值是否存在
                        if key_val in seen_keys:
                            # 检查是否有完全相同的行
                            for existing_row in seen_keys[key_val]:
                                if existing_row == row:
                                    existing = True
                                    break
                            
                            # 记录不同值的重复键
                            if not existing:
                                self.duplicates.append({
                                    'key': key_val,
                                    'file1': seen_keys[key_val][0][0],
                                    'file2': os.path.basename(file),
                                    'values1': seen_keys[key_val][0],
                                    'values2': row
                                })
                        
                        # 添加新行
                        if not existing:
                            seen_keys[key_val].append(row)
                            self.merged_data.append(row)
            except Exception as e:
                return False, f"读取文件 {os.path.basename(file)} 失败: {str(e)}"
        
        # 数据排序
        if sort_column:
            sort_column_upper = sort_column.strip().upper()
            if sort_column_upper in self.headers:
                sort_index = self.headers.index(sort_column_upper)
                try:
                    self.merged_data.sort(
                        key=lambda x: x[sort_index] if x[sort_index] else "", 
                        reverse=not ascending
                    )
                except Exception as e:
                    return False, f"排序失败: {str(e)}"
        
        # 生成统计信息
        stats = f"合并完成!\n"
        stats += f"总文件数: {len(self.files)}\n"
        stats += f"总读取行数: {total_rows}\n"
        
        if filter_zero:
            stats += f"过滤无效行: {invalid_count}\n"
        
        if abnormal_columns:
            stats += f"过滤异常行: {abnormal_count}\n"
        
        stats += f"合并后有效行数: {len(self.merged_data)}\n"
        
        if self.merged_data:
            key_counts = len(set(row[key_index] for row in self.merged_data))
            stats += f"唯一键值数: {key_counts}\n"
        
        if self.duplicates:
            stats += f"\n警告: 发现 {len(self.duplicates)} 个键值冲突\n"
            stats += f"已保留所有不同值的行\n"
        
        return True, stats

    def save_to_file(self, output_path):
        """保存合并结果到文件"""
        if not self.merged_data:
            return False, "错误: 没有数据可保存"
        
        try:
            with open(output_path, 'w', encoding='utf-8-sig', newline='') as f:
                writer = csv.writer(f)
                # 使用原始列名作为标题
                writer.writerow(self.raw_headers)
                writer.writerows(self.merged_data)
            return True, f"成功保存到: {output_path}"
        except Exception as e:
            return False, f"保存文件失败: {str(e)}"


class CSV_Merger_App(QMainWindow):
    def __init__(self):
        super().__init__()
        self.merger = CSV_Merger()
        self.setWindowTitle("CSV 文件合并工具 (带数据校验和异常检测)")
        self.setGeometry(100, 100, 1200, 800)
        
        # 创建主控件和布局
        main_widget = QWidget()
        main_layout = QVBoxLayout()
        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)
        
        # 创建分割器
        splitter = QSplitter(Qt.Horizontal)
        main_layout.addWidget(splitter)
        
        # 左侧面板 - 文件操作
        left_panel = QWidget()
        left_layout = QVBoxLayout()
        left_panel.setLayout(left_layout)
        
        # 右侧面板 - 合并设置和日志
        right_panel = QWidget()
        right_layout = QVBoxLayout()
        right_panel.setLayout(right_layout)
        
        splitter.addWidget(left_panel)
        splitter.addWidget(right_panel)
        splitter.setSizes([300, 900])
        
        # ========== 左侧面板内容 ==========
        file_group = QGroupBox("文件操作")
        file_layout = QVBoxLayout()
        file_group.setLayout(file_layout)
        left_layout.addWidget(file_group)
        
        # 单个文件添加
        single_file_layout = QHBoxLayout()
        self.file_path_edit = QLineEdit()
        self.file_path_edit.setPlaceholderText("选择单个CSV文件...")
        self.browse_btn = QPushButton("浏览")
        self.browse_btn.clicked.connect(self.browse_file)
        single_file_layout.addWidget(self.file_path_edit)
        single_file_layout.addWidget(self.browse_btn)
        file_layout.addLayout(single_file_layout)
        
        # 批量添加
        batch_layout = QHBoxLayout()
        self.dir_edit = QLineEdit()
        self.dir_edit.setPlaceholderText("选择搜索目录...")
        self.dir_browse_btn = QPushButton("浏览")
        self.dir_browse_btn.clicked.connect(self.browse_directory)
        batch_layout.addWidget(self.dir_edit)
        batch_layout.addWidget(self.dir_browse_btn)
        file_layout.addLayout(batch_layout)
        
        keyword_layout = QHBoxLayout()
        self.keyword_edit = QLineEdit()
        self.keyword_edit.setPlaceholderText("文件名关键词(可选)...")
        self.find_btn = QPushButton("查找并添加")
        self.find_btn.clicked.connect(self.find_and_add_files)
        keyword_layout.addWidget(self.keyword_edit)
        keyword_layout.addWidget(self.find_btn)
        file_layout.addLayout(keyword_layout)
        
        # 文件列表
        file_list_label = QLabel("已选文件列表:")
        file_layout.addWidget(file_list_label)
        
        self.file_list = QListWidget()
        self.file_list.setSelectionMode(QListWidget.ExtendedSelection)
        file_layout.addWidget(self.file_list)
        
        # 文件列表按钮
        file_btn_layout = QHBoxLayout()
        self.remove_btn = QPushButton("移除选中")
        self.remove_btn.clicked.connect(self.remove_selected_files)
        self.clear_btn = QPushButton("清空列表")
        self.clear_btn.clicked.connect(self.clear_file_list)
        file_btn_layout.addWidget(self.remove_btn)
        file_btn_layout.addWidget(self.clear_btn)
        file_layout.addLayout(file_btn_layout)
        
        # 列名检查
        self.check_headers_btn = QPushButton("检查列名一致性")
        self.check_headers_btn.clicked.connect(self.check_headers)
        left_layout.addWidget(self.check_headers_btn)
        
        # 数据查看按钮
        data_btn_layout = QHBoxLayout()
        self.view_invalid_btn = QPushButton("查看无效数据")
        self.view_invalid_btn.clicked.connect(self.view_invalid_data)
        self.view_invalid_btn.setEnabled(False)
        data_btn_layout.addWidget(self.view_invalid_btn)
        
        self.view_abnormal_btn = QPushButton("查看异常数据")
        self.view_abnormal_btn.clicked.connect(self.view_abnormal_data)
        self.view_abnormal_btn.setEnabled(False)
        data_btn_layout.addWidget(self.view_abnormal_btn)
        
        left_layout.addLayout(data_btn_layout)
        
        # ========== 右侧面板内容 ==========
        # 合并设置
        merge_group = QGroupBox("合并设置")
        merge_layout = QVBoxLayout()
        merge_group.setLayout(merge_layout)
        right_layout.addWidget(merge_group)
        
        # 数据校验设置
        validation_layout = QHBoxLayout()
        self.filter_zero_checkbox = QCheckBox("过滤无效数据（非键值列全为0或空）")
        self.filter_zero_checkbox.setChecked(True)
        validation_layout.addWidget(self.filter_zero_checkbox)
        merge_layout.addLayout(validation_layout)
        
        # 异常检测设置
        abnormal_layout = QHBoxLayout()
        self.abnormal_detection_checkbox = QCheckBox("检测并过滤异常数据（指定列全部相同）")
        self.abnormal_detection_checkbox.setChecked(True)
        abnormal_layout.addWidget(self.abnormal_detection_checkbox)
        
        self.abnormal_columns_edit = QLineEdit()
        self.abnormal_columns_edit.setPlaceholderText("列名（用逗号分隔，如:IM,IF,IC,IH）")
        self.abnormal_columns_edit.setText("IM,IF,IC,IH")
        abnormal_layout.addWidget(self.abnormal_columns_edit)
        
        merge_layout.addLayout(abnormal_layout)
        
        # 键值列选择
        key_layout = QHBoxLayout()
        key_layout.addWidget(QLabel("合并键值列:"))
        self.key_column_combo = QComboBox()
        self.key_column_combo.setEditable(True)
        key_layout.addWidget(self.key_column_combo)
        merge_layout.addLayout(key_layout)
        
        # 排序设置
        sort_group = QGroupBox("排序设置")
        sort_layout = QVBoxLayout()
        sort_group.setLayout(sort_layout)
        merge_layout.addWidget(sort_group)
        
        sort_enable_layout = QHBoxLayout()
        sort_enable_layout.addWidget(QLabel("启用排序:"))
        self.sort_checkbox = QCheckBox()
        self.sort_checkbox.setChecked(True)
        sort_enable_layout.addWidget(self.sort_checkbox)
        sort_layout.addLayout(sort_enable_layout)
        
        sort_column_layout = QHBoxLayout()
        sort_column_layout.addWidget(QLabel("排序列:"))
        self.sort_column_combo = QComboBox()
        self.sort_column_combo.setEditable(True)
        sort_column_layout.addWidget(self.sort_column_combo)
        sort_layout.addLayout(sort_column_layout)
        
        sort_order_layout = QHBoxLayout()
        sort_order_layout.addWidget(QLabel("排序方式:"))
        self.asc_radio = QRadioButton("升序")
        self.desc_radio = QRadioButton("降序")
        self.asc_radio.setChecked(True)
        sort_order_layout.addWidget(self.asc_radio)
        sort_order_layout.addWidget(self.desc_radio)
        sort_layout.addLayout(sort_order_layout)
        
        # 输出设置
        output_layout = QHBoxLayout()
        output_layout.addWidget(QLabel("输出文件:"))
        self.output_edit = QLineEdit()
        self.output_edit.setPlaceholderText("选择输出文件路径...")
        self.output_browse_btn = QPushButton("浏览")
        self.output_browse_btn.clicked.connect(self.browse_output)
        output_layout.addWidget(self.output_edit)
        output_layout.addWidget(self.output_browse_btn)
        merge_layout.addLayout(output_layout)
        
        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        right_layout.addWidget(self.progress_bar)
        
        # 操作按钮
        btn_layout = QHBoxLayout()
        self.merge_btn = QPushButton("开始合并")
        self.merge_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
        self.merge_btn.clicked.connect(self.start_merge)
        btn_layout.addWidget(self.merge_btn)
        right_layout.addLayout(btn_layout)
        
        # 日志区域
        log_label = QLabel("操作日志:")
        right_layout.addWidget(log_label)
        
        self.log_area = QTextEdit()
        self.log_area.setReadOnly(True)
        right_layout.addWidget(self.log_area)
        
        # 状态栏
        self.status_bar = self.statusBar()
        self.status_bar.showMessage("就绪")
        
        # 初始化
        self.update_column_combos()

    def log_message(self, message):
        """将消息添加到日志区域"""
        self.log_area.append(message)
        self.log_area.ensureCursorVisible()
        QApplication.processEvents()
    
    def update_column_combos(self):
        """更新列名组合框"""
        if self.merger.files:
            raw_headers = self.merger.get_raw_headers(self.merger.files[0])
            if raw_headers:
                self.key_column_combo.clear()
                self.sort_column_combo.clear()
                self.key_column_combo.addItems(raw_headers)
                self.sort_column_combo.addItems(raw_headers)
                if raw_headers:
                    self.key_column_combo.setCurrentIndex(0)
                    self.sort_column_combo.setCurrentIndex(0)
    
    def browse_file(self):
        """浏览并添加单个文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择CSV文件", "", "CSV文件 (*.csv);;所有文件 (*)"
        )
        if file_path:
            self.file_path_edit.setText(file_path)
            self.merger.files.append(file_path)
            self.file_list.addItem(os.path.basename(file_path))
            self.update_column_combos()
            self.log_message(f"添加文件: {os.path.basename(file_path)}")
    
    def browse_directory(self):
        """浏览并选择目录"""
        directory = QFileDialog.getExistingDirectory(self, "选择搜索目录")
        if directory:
            self.dir_edit.setText(directory)
    
    def find_and_add_files(self):
        """查找并添加文件"""
        directory = self.dir_edit.text()
        keyword = self.keyword_edit.text()
        
        if not directory or not os.path.isdir(directory):
            QMessageBox.warning(self, "错误", "请选择有效的搜索目录")
            return
        
        files = self.merger.find_files(directory, keyword)
        if not files:
            QMessageBox.information(self, "信息", "没有找到匹配的文件")
            return
        
        # 添加新文件（避免重复）
        new_files = 0
        for file in files:
            if file not in self.merger.files:
                self.merger.files.append(file)
                self.file_list.addItem(os.path.basename(file))
                new_files += 1
        
        if new_files > 0:
            self.log_message(f"添加了 {new_files} 个文件")
            self.update_column_combos()
        else:
            QMessageBox.information(self, "信息", "没有新文件可添加")
    
    def remove_selected_files(self):
        """移除选中的文件"""
        selected_items = self.file_list.selectedItems()
        if not selected_items:
            return
        
        for item in selected_items:
            row = self.file_list.row(item)
            self.file_list.takeItem(row)
            del self.merger.files[row]
        
        self.log_message(f"移除了 {len(selected_items)} 个文件")
        self.update_column_combos()
    
    def clear_file_list(self):
        """清空文件列表"""
        if not self.merger.files:
            return
        
        self.file_list.clear()
        self.merger.files = []
        self.log_message("已清空文件列表")
        self.update_column_combos()
    
    def check_headers(self):
        """检查列名一致性"""
        if not self.merger.files:
            QMessageBox.warning(self, "错误", "没有选择文件")
            return
        
        success, message = self.merger.check_headers_consistency()
        if success:
            QMessageBox.information(self, "列名检查", message)
        else:
            # 使用更宽的对话框显示不一致的详细信息
            msg_box = QMessageBox(self)
            msg_box.setIcon(QMessageBox.Warning)
            msg_box.setWindowTitle("列名检查")
            msg_box.setText(message)
            msg_box.setStandardButtons(QMessageBox.Ok)
            msg_box.exec_()
        
        self.log_message("执行列名检查: " + ("成功" if success else "失败"))
    
    def browse_output(self):
        """选择输出文件路径"""
        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存合并文件", "", "CSV文件 (*.csv);;所有文件 (*)"
        )
        if file_path:
            self.output_edit.setText(file_path)
    
    def start_merge(self):
        """开始合并过程"""
        if len(self.merger.files) < 2:
            QMessageBox.warning(self, "错误", "请至少选择2个文件")
            return
        
        key_column = self.key_column_combo.currentText()
        if not key_column:
            QMessageBox.warning(self, "错误", "请选择合并键值列")
            return
        
        sort_column = None
        ascending = True
        
        if self.sort_checkbox.isChecked():
            sort_column = self.sort_column_combo.currentText()
            ascending = self.asc_radio.isChecked()
        
        output_path = self.output_edit.text()
        if not output_path:
            QMessageBox.warning(self, "错误", "请选择输出文件路径")
            return
        
        # 获取数据校验选项
        filter_zero = self.filter_zero_checkbox.isChecked()
        
        # 获取异常检测选项
        abnormal_columns = None
        if self.abnormal_detection_checkbox.isChecked():
            columns_str = self.abnormal_columns_edit.text().strip()
            if columns_str:
                abnormal_columns = [col.strip() for col in columns_str.split(',') if col.strip()]
                self.log_message(f"启用异常检测，检测列: {', '.join(abnormal_columns)}")
        
        # 显示进度条
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 0)  # 不确定进度
        
        try:
            # 执行合并
            self.log_message("开始合并文件...")
            if filter_zero:
                self.log_message("启用数据校验：过滤非键值列全为0或空的行")
                
            if abnormal_columns:
                self.log_message("启用异常检测：过滤指定列全部相同的行")
                
            success, message = self.merger.merge_files(
                key_column, 
                sort_column, 
                ascending, 
                filter_zero,
                abnormal_columns
            )
            
            if success:
                self.log_message(message)
                
                # 保存文件
                self.log_message("保存结果文件...")
                save_success, save_message = self.merger.save_to_file(output_path)
                
                if save_success:
                    self.log_message(save_message)
                    self.log_message("合并完成!")
                    self.status_bar.showMessage("合并成功完成")
                    
                    # 如果有无效数据，启用查看按钮
                    if filter_zero and self.merger.invalid_rows:
                        self.view_invalid_btn.setEnabled(True)
                        self.log_message(f"发现 {len(self.merger.invalid_rows)} 行无效数据，可点击'查看无效数据'按钮查看")
                    
                    # 如果有异常数据，启用查看按钮
                    if abnormal_columns and self.merger.abnormal_rows:
                        self.view_abnormal_btn.setEnabled(True)
                        self.log_message(f"发现 {len(self.merger.abnormal_rows)} 行异常数据，可点击'查看异常数据'按钮查看")
                    
                    QMessageBox.information(self, "成功", "文件合并成功完成")
                else:
                    self.log_message(save_message)
                    QMessageBox.critical(self, "错误", save_message)
            else:
                self.log_message(message)
                QMessageBox.critical(self, "错误", message)
        
        except Exception as e:
            self.log_message(f"发生错误: {str(e)}")
            QMessageBox.critical(self, "错误", f"发生错误: {str(e)}")
        
        finally:
            # 隐藏进度条
            self.progress_bar.setVisible(False)
    
    def view_invalid_data(self):
        """查看无效数据"""
        if not self.merger.invalid_rows:
            QMessageBox.information(self, "信息", "没有无效数据")
            return
        
        # 创建新窗口显示无效数据
        invalid_dialog = QDialog(self)
        invalid_dialog.setWindowTitle("无效数据详情")
        invalid_dialog.setGeometry(200, 200, 800, 600)
        
        layout = QVBoxLayout()
        invalid_dialog.setLayout(layout)
        
        # 显示统计信息
        stats_label = QLabel(f"发现 {len(self.merger.invalid_rows)} 行无效数据（非键值列全为0或空）")
        layout.addWidget(stats_label)
        
        # 创建表格显示无效数据
        table = QTableWidget()
        layout.addWidget(table)
        
        # 设置表格
        table.setRowCount(len(self.merger.invalid_rows))
        table.setColumnCount(len(self.merger.raw_headers) + 1)  # 增加一列显示文件名
        
        # 设置表头
        headers = ["文件名"] + self.merger.raw_headers
        table.setHorizontalHeaderLabels(headers)
        
        # 填充数据
        for row_idx, invalid_data in enumerate(self.merger.invalid_rows):
            # 文件名
            table.setItem(row_idx, 0, QTableWidgetItem(invalid_data['file']))
            
            # 行数据
            for col_idx, value in enumerate(invalid_data['row']):
                item = QTableWidgetItem(value)
                
                # 标记为红色
                item.setForeground(Qt.red)
                table.setItem(row_idx, col_idx + 1, item)
        
        # 调整列宽
        table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        
        # 添加关闭按钮
        close_btn = QPushButton("关闭")
        close_btn.clicked.connect(invalid_dialog.close)
        layout.addWidget(close_btn)
        
        invalid_dialog.exec_()
    
    def view_abnormal_data(self):
        """查看异常数据"""
        if not self.merger.abnormal_rows:
            QMessageBox.information(self, "信息", "没有异常数据")
            return
        
        # 获取用户设置的异常检测列
        columns_str = self.abnormal_columns_edit.text().strip()
        abnormal_columns = [col.strip() for col in columns_str.split(',') if col.strip()]
        
        # 创建新窗口显示异常数据
        abnormal_dialog = QDialog(self)
        abnormal_dialog.setWindowTitle("异常数据详情")
        abnormal_dialog.setGeometry(200, 200, 800, 600)
        
        layout = QVBoxLayout()
        abnormal_dialog.setLayout(layout)
        
        # 显示统计信息
        stats_label = QLabel(f"发现 {len(self.merger.abnormal_rows)} 行异常数据（{', '.join(abnormal_columns)}列全部相同）")
        layout.addWidget(stats_label)
        
        # 创建表格显示异常数据
        table = QTableWidget()
        layout.addWidget(table)
        
        # 设置表格
        table.setRowCount(len(self.merger.abnormal_rows))
        table.setColumnCount(len(self.merger.raw_headers) + 1)  # 增加一列显示文件名
        
        # 设置表头
        headers = ["文件名"] + self.merger.raw_headers
        table.setHorizontalHeaderLabels(headers)
        
        # 填充数据
        for row_idx, abnormal_data in enumerate(self.merger.abnormal_rows):
            # 文件名
            table.setItem(row_idx, 0, QTableWidgetItem(abnormal_data['file']))
            
            # 行数据
            for col_idx, value in enumerate(abnormal_data['row']):
                item = QTableWidgetItem(value)
                
                # 如果是异常列，标记为橙色
                try:
                    # 使用标准化列名比较
                    col_name = self.merger.headers[col_idx]
                    if col_name in [col.upper() for col in abnormal_columns]:
                        item.setForeground(Qt.darkYellow)
                except IndexError:
                    pass
                
                table.setItem(row_idx, col_idx + 1, item)
        
        # 调整列宽
        table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        
        # 添加关闭按钮
        close_btn = QPushButton("关闭")
        close_btn.clicked.connect(abnormal_dialog.close)
        layout.addWidget(close_btn)
        
        abnormal_dialog.exec_()


if __name__ == "__main__":
    # 自动安装依赖
    try:
        import chardet
        import pandas
        from PyQt5.QtWidgets import QDialog
    except ImportError:
        import sys
        import subprocess
        print("正在安装所需依赖...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", "chardet", "pandas", "PyQt5"])
    
    app = QApplication(sys.argv)
    window = CSV_Merger_App()
    window.show()
    sys.exit(app.exec_())